package fatboy.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;

import junit.framework.Assert;

import org.junit.Test;

import fatboy.addons.Broadcast;
import fatboy.hypeerweb.Connection;
import fatboy.hypeerweb.Node;
import fatboy.hypeerweb.NodeAddress;

/**
 * Black Box testing of Broadcast Class.
 * 
 * In the test below I'm testing the following:
 * 	1) For my Boundary Value testing (with a boundary hypeerweb size of 1 node)
 * 		 I used the following test cases:
 * 		 a. Empty HypeerWeb (zero nodes, just outside the boundary)
 * 			- erroneous condition, expected correct error handling
 * 		 b. Single Node (single node, boundary value)
 * 			- expected no output, because doesn't broadcast to self
 *		 c. Broadcast with two nodes (just above boundary value)
 *			- expected correct message delivery
 *		 d. Broadcast with many nodes in the HypeerWeb (far above the
 *			 boundary).
 *	2) For Informational Domain Testing I tested sending the broadcast from ever Node
 *		in the hypeerweb to every other node in the hypeerweb in perfect 
 *		 and incomplete hypeerwebs thus getting all cases of interior, last, edge and surface nodes
 *	3) For Random testing I test a hypeerwen of a random size and call broadcast from everynode 
 *		and make sure it makes it to every node.
 * Input domain: NodeAddress >=0
 * 
 * @author Jack Quincy
 */
public class JacksBlackBoxTesting {
	/**
	 * makes sure it functions properly if it is the only node in the web
	 * expected output: nothing
	 */
	@Test
	public void singleNodeTestOfBroadCast(){
		//boundary value
		Connection connect = setupWeb(1);
		
		List<Node> web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
	}
	/**
	 * checks that broadcast works from any node in a two node tree  
	 * expected output: printout at node 1 then at node 0
	 */
	@Test
	public void twoNodeTestOfBroadCast(){
		//+1 perfect case
		final int two = 2;
		Connection connect = setupWeb(two);
		
		List<Node> web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
	}
	/**
	 * checks that broadcast works from any node in a two node tree  
	 * expected output: six statments
	 */
	@Test
	public void threeNodeTestOfBroadCast(){
		//small imperfect case
		final int three = 3;
		Connection connect = setupWeb(three);
		
		List<Node> web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
	}
	/**
	 * tests that the broadcast sends the message to every node in the tree
	 *  for any node a perfect hypeerweb
	 */
	@Test
	public void perfetHypeerWebState(){
		//large perfect case
		final int sixtyfour = 64;
		Connection connect = setupWeb(sixtyfour);
		
		List<Node> web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
	}
	/**
	 * tests that the broadcast sends the message to every node in the tree
	 *  for any node in a random hypeerweb
	 */
	public void randomTest(){
		//random hypeerweb
		Random rand = new Random();
		int random = Math.abs(rand.nextInt()%100);
		Connection connect = setupWeb(random);
		
		List<Node> web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
	}
	/**
	 * tests that the broadcast sends the message to every node in the tree
	 *  for any node a incomplete hypeerweb 
	 */
	@Test
	public void incompleteHypeerWebState(){
		// far from perfect
		final int fiftythree = 53;
		Connection connect = setupWeb(fiftythree);
		
		List<Node> web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
		//one more than perfect
		final int sixtyfive = 65;
		connect = setupWeb(sixtyfive );
		
		 web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
		//one less than perfect
		final int sixtythree = 63;
		 connect = setupWeb(sixtythree);
		
		 web = connect.getAllNodes();
		for(Node n : web){
			File output = getOutput();
			Set<broadcastOutput> correct = generateCorrect(n,web);
			n.accept(new Broadcast(), n);
			verifyOutput(output,correct);
		}
	}
	private Set<broadcastOutput> generateCorrect(Node n, List<Node> web) {
		NodeAddress origin = n.getAddress();
		Set<broadcastOutput> ret = new TreeSet<broadcastOutput>();
		for(Node t : web){
			if(t == n){
				continue;
			}
			NodeAddress spot = t.getAddress();
			ret.add(new broadcastOutput(origin,spot));
		}
		return ret;
	}
	static File getOutput() {
		PrintStream ps = null;
		File output = new File("test.txt");
		try {
			ps = new PrintStream(output);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.setOut(ps);
		return output;
	}
	private void verifyOutput(File output, Set<broadcastOutput> correct) {
		Scanner scan = null;
		try {
			scan = new Scanner(output);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int lineCount = 0;
		while(scan.hasNext()){
			String s = scan.nextLine();
			Assert.assertTrue(correct.contains(new broadcastOutput(s)));
			lineCount++;
		}
		Assert.assertEquals(correct.size(), lineCount);
		
	}
	static Connection setupWeb(int i){
		Connection connect = new Connection();
		for(int j = 0; j < i; j++){
			connect.addNode(j);
		}
		return connect;
	}
	private class broadcastOutput implements Comparable{
		private NodeAddress origin;
		private NodeAddress spot;
		public broadcastOutput(String line){
			Scanner scan = new Scanner(line);
			scan.useDelimiter("[(-)]");
			scan.next();
			origin = new NodeAddress(Integer.parseInt(scan.next()));
			scan.next();
			spot = new NodeAddress(Integer.parseInt(scan.next()));
		}
		public broadcastOutput(NodeAddress o, NodeAddress s){
			
			origin = o;
			
			spot = s;
		}
		public boolean equals(Object o){
			if(!(o instanceof broadcastOutput)){
				return false;
			}
			broadcastOutput in = (broadcastOutput) o;
			return spot.equals(in.spot);
		}
		public int compareTo(Object o){
			if(!(o instanceof broadcastOutput)){
				return -1;
			}
			broadcastOutput in = (broadcastOutput) o;
			
			return spot.compareTo(in.spot);
		}
	}
}
